using Joy.Erp.BasicArchives.Core;
using Joy.Erp.BasicArchives.Core.Filters;
using Joy.Erp.BasicArchives.Permissions;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;

namespace Joy.Erp.BasicArchives.Asns
{
    [Authorize]
    public class AsnAppService : BasicArchivesAppService, IAsnAppService
    {
        protected IAsnRepository AsnRepository => LazyServiceProvider.LazyGetRequiredService<IAsnRepository>();
        protected AsnManager AsnManager => LazyServiceProvider.LazyGetRequiredService<AsnManager>();

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<AsnDto> GetAsync(Guid id)
        {
            var entity = await AsnRepository.GetAsync(id, true);
            return ObjectMapper.Map<Asn, AsnDto>(entity);
        }

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<AsnLineDto> GetChildAsync(Guid id)
        {
            var child = await AsnRepository.GetChildAsync(id, true);
            return ObjectMapper.Map<AsnLine, AsnLineDto>(child);
        }

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<AsnDto> FindAsync(string filter)
        {
            var entity = await AsnRepository.FindAsync(x => x.Code == filter);
            return ObjectMapper.Map<Asn, AsnDto>(entity);
        }

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<PagedResultDto<AsnDto>> GetListAsync(FilterRequestDto input)
        {
            var totalCount = await AsnRepository.GetCountAsync(input.Filter, true);
            var list = await AsnRepository.GetPagedListAsync(input.Filter, input.SkipCount, input.MaxResultCount, input.Sorting, true);

            return new PagedResultDto<AsnDto>(
                totalCount,
                ObjectMapper.Map<List<Asn>, List<AsnDto>>(list));
        }

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<PagedResultDto<AsnDto>> QueryAsync(FilterGroupRequestDto input)
        {
            Expression<Func<Asn, bool>> expression = null;
            if (input != null && input.FilterGroup != null)
            {
                expression = FilterConverter.Convert<Asn>(input.FilterGroup);
            }

            var totalCount = await AsnRepository.GetCountAsync(expression, true);
            var list = await AsnRepository.GetPagedListAsync(expression, input.SkipCount, input.MaxResultCount, input.Sorting, true);

            return new PagedResultDto<AsnDto>(
                totalCount,
                ObjectMapper.Map<List<Asn>, List<AsnDto>>(list));
        }

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<PagedResultDto<AsnLineDto>> QueryChildrenAsync(FilterGroupRequestDto input)
        {
            Expression<Func<AsnLine, bool>> expression = null;
            if (input != null && input.FilterGroup != null)
            {
                expression = FilterConverter.Convert<AsnLine>(input.FilterGroup);
            }

            var totalCount = await AsnRepository.GetChildrenCountAsync(expression, true);
            var list = await AsnRepository.GetChildrenPagedListAsync(expression, input.SkipCount, input.MaxResultCount, input.Sorting, true);

            return new PagedResultDto<AsnLineDto>(
                totalCount,
                ObjectMapper.Map<List<AsnLine>, List<AsnLineDto>>(list));
        }

        [Authorize(BasicArchivesPermissions.Asn.Query)]
        public async Task<AsnDto> FindBySourceKeyAsync(string sourceKey, string sourceType)
        {
            var entity = await AsnRepository.FindAsync(x => x.SourceKey == sourceKey && x.SourceType == sourceType);
            return ObjectMapper.Map<Asn, AsnDto>(entity);
        }

        [Authorize(BasicArchivesPermissions.Asn.Create)]
        public async Task<AsnDto> CreateAsync(AsnCreateDto input)
        {
            var entity = ObjectMapper.Map<AsnCreateDto, Asn>(input);

            await AsnManager.CreateAsync(entity, true);
            return await GetAsync(entity.Id);
        }

        [Authorize(BasicArchivesPermissions.Asn.Update)]
        public async Task<AsnDto> UpdateAsync(Guid id, AsnUpdateDto input)
        {
            var entity = await AsnRepository.GetAsync(id, true);
            entity.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);

            await AsnRepository.EnsureCollectionLoadedAsync(entity, x => x.Children);
            ObjectMapper.Map(input, entity);

            await AsnManager.UpdateAsync(entity, true);
            return await GetAsync(entity.Id);
        }

        [Authorize(BasicArchivesPermissions.Asn.Submit)]
        public async Task<AsnDto> SubmitAsync(Guid id)
        {
            await AsnManager.ChangeStatus(id, DocumentStatus.Waiting);
            return await GetAsync(id);
        }

        [Authorize(BasicArchivesPermissions.Asn.Approve)]
        public async Task<AsnDto> ApproveAsync(Guid id)
        {
            await AsnManager.ChangeStatus(id, DocumentStatus.Approved);
            return await GetAsync(id);
        }

        [Authorize(BasicArchivesPermissions.Asn.UnApprove)]
        public async Task<AsnDto> UnApproveAsync(Guid id)
        {
            await AsnManager.ChangeStatus(id, DocumentStatus.Open);
            return await GetAsync(id);
        }

        [Authorize(BasicArchivesPermissions.Asn.Delete)]
        public async Task DeleteAsync(Guid id)
        {
            await AsnManager.DeleteAsync(id);
        }

        [Authorize(BasicArchivesPermissions.Asn.Create)]
        public async Task<List<AsnDto>> BatchCreateAsync(List<AsnCreateDto> inputs, bool useBulk = false)
        {
            var entities = inputs
                .Select(ObjectMapper.Map<AsnCreateDto, Asn>)
                .ToList();

            await AsnManager.CreateManyAsync(entities, useBulk, autoSave: true);
            return ObjectMapper.Map<List<Asn>, List<AsnDto>>(entities);
        }

        [Authorize(BasicArchivesPermissions.Asn.Update)]
        public async Task<List<AsnDto>> BatchUpdateAsync(List<AsnBatchUpdateDto> inputs, bool useBulk = false)
        {
            if (inputs.Count == 0) return new List<AsnDto>();

            var ids = inputs.Select(x => x.Id).ToList();
            var query = await AsnRepository.WithDetailsAsync(x => x.Children);
            var entities = await AsyncExecuter.ToListAsync(query.Where(x => ids.Contains(x.Id)));

            if (useBulk)
            {
                foreach (var entity in entities)
                {
                    var input = inputs.FirstOrDefault(x => entity.Id == x.Id);
                    if (input == null) continue;

                    entity.Code = input.Data.Code;
                    entity.BusinessDate = input.Data.BusinessDate;
                    entity.DocumentStatus = input.Data.DocumentStatus;
                    entity.SourceCreatedOn = input.Data.SourceCreatedOn;

                    entity.OrderTypeId = input.Data.OrderTypeId;
                    entity.SupplierId = input.Data.SupplierId;
                    entity.ShipmentContact = input.Data.ShipmentContact;
                    entity.Phone = input.Data.Phone;
                    entity.LicensePlate = input.Data.LicensePlate;
                    entity.ShipmentDate = input.Data.ShipmentDate;
                    entity.ArriveDate = input.Data.ArriveDate;
                    entity.Mark = input.Data.Mark;

                    // 删除逻辑：删除源数据中不存在的实体
                    var childrenToRemove = entity.Children.Where(destChild =>
                    !input.Data.Children.Any(srcChild => srcChild.Id.HasValue && srcChild.Id.Value == destChild.Id)).ToList();
                    foreach (var child in childrenToRemove)
                    {
                        entity.Children.Remove(child);
                    }

                    // 更新和新增子项逻辑
                    foreach (var srcChild in input.Data.Children)
                    {
                        var existingChild = srcChild.Id.HasValue && srcChild.Id.Value != Guid.Empty
                            ? entity.Children.FirstOrDefault(destItem => destItem.Id == srcChild.Id.Value)
                            : null;

                        if (existingChild != null)
                        {
                            existingChild.LineNo = srcChild.LineNo;
                            existingChild.ItemMasterId = srcChild.ItemMasterId;
                            existingChild.ItemCode = srcChild.ItemCode;
                            existingChild.ItemName = srcChild.ItemName;
                            existingChild.ItemSpec = srcChild.ItemSpec;
                            existingChild.Quantity = srcChild.Quantity;
                            existingChild.PurchasingQuantity = srcChild.PurchasingQuantity;
                            existingChild.ReceivedQuantity = srcChild.ReceivedQuantity;
                            existingChild.UnitId = srcChild.UnitId;
                            existingChild.SourceCreatedOn = srcChild.SourceCreatedOn;
                        }
                        else
                        {
                            var newLine = new AsnLine
                            {
                                SourceType = srcChild.SourceType,
                                SourceKey = srcChild.SourceKey,
                                SourceCreatedOn = srcChild.SourceCreatedOn,
                                LineNo = srcChild.LineNo,
                                ItemMasterId = srcChild.ItemMasterId,
                                ItemCode = srcChild.ItemCode,
                                ItemName = srcChild.ItemName,
                                ItemSpec = srcChild.ItemSpec,
                                Quantity = srcChild.Quantity,
                                PurchasingQuantity = srcChild.PurchasingQuantity,
                                ReceivedQuantity = srcChild.ReceivedQuantity,
                                UnitId = srcChild.UnitId,
                                DocumentId = entity.Id,
                                CreationTime = Clock.Now,
                                CreatorId = CurrentUser.Id,
                            };

                            newLine.SetId(GuidGenerator.Create());
                            entity.Children.Add(newLine);
                        }
                    }
                }

                using (var uow = UnitOfWorkManager.Begin(new Volo.Abp.Uow.AbpUnitOfWorkOptions(), requiresNew: true))
                {
                    await AsnRepository.BulkUpdateAsync(entities);
                    await uow.CompleteAsync();
                }
            }
            else
            {
                foreach (var entity in entities)
                {
                    var input = inputs.FirstOrDefault(x => entity.Id == x.Id);
                    if (input == null) continue;

                    entity.SetConcurrencyStampIfNotNull(input.Data.ConcurrencyStamp);
                    ObjectMapper.Map(input.Data, entity);
                }
                await AsnRepository.UpdateManyAsync(entities);
            }

            return ObjectMapper.Map<List<Asn>, List<AsnDto>>(entities);
        }

        [Authorize(BasicArchivesPermissions.Asn.Delete)]
        public async Task BatchDeleteAsync(List<Guid> ids, bool useBulk = false)
        {
            if (ids.Count == 0) return;

            var query = await AsnRepository.WithDetailsAsync(x => x.Children);
            var entities = await AsyncExecuter.ToListAsync(query.Where(x => ids.Contains(x.Id)));

            await AsnManager.DeleteManyAsync(entities, useBulk, autoSave: true);
        }
    }
}